@file:JvmName("jian") //修改生成的java类的名字 注意必须要写在包的外面
import java.io.File
import java.io.IOException
import kotlin.math.roundToInt
import kotlin.reflect.KProperty

/**
@author Jiange
@create 2022-01-15-17:19
 */
fun main(){
    var a:Int = 10
    var b:String = "dsadda"
    var c:Char = 'd'
    a = 10; c='a'
    println(a)

    test4(flag = false, age = 12)
    `8928dadspecial`()
    val d = 10
}
//编译时常量 只读变量
//只能在函数之外定义。因为编译时常量必须在编译时赋值，而函数只有在运行时才调用，函数中的变量也是在运行时赋值，编译常量要在变量赋值之前就存在
const val e = 10

//range表达式
//in关键字检查m某个值是否在指定范围内
fun test1(){
    val age = 4
    if (age in 0..3) {
        println(message = age)
    } else if (age in 3..12) {
        println(message = age)
    } else {
        println(message = age)
    }

    if(age !in 0..3){

    }
}

//when表达式
fun test2(){
    val school = ""
    var level = when(school) {
        "学前班" -> "幼儿"
        "小学" -> "少儿"
        else -> {
            print("")
        }
    }
}

//模板字符串
fun test3(){
    val origin = "Jack"
    val desk = "Rose"
    println("$origin dasd $desk")

    val flag = false
    println("Answer is ${if (flag) "正确" else "错误"}")
}


//函数
public fun test4(age:Int = 12, flag:Boolean):String{

    return ""
    //在kotlin 没有返回值的类型叫做Unit  Nothing
}
public fun `8928dadspecial`():Unit{

}
fun test5(){
    val total = "jodaoidioa".count()
    "dsadsa".count({c: Char -> c == 'd' })
    val a : (String) -> String = {
        val a = "dsa"
        /*
        会自动返回最后一行
        当有一个参数时可以使用it来获取
        有多个时就不能用了
        * */
        "$a $it"
    }

    val b = {name:String, age:Int ->
        "$name $age"
    }

    val c : (String, Int) -> String = { name, age ->
        /*
        有多个参数的时候就得写全参数名
         */
        "$name ${age+1}"
    }
    //匿名函数不允许用命名参数

    val d : (String, Int, (String, Int)->String) -> String = { name, age, dis ->
        dis(name, age)
    }
    //当lambda表达式是唯一的参数或者是最后的参数时，括号可以省略
    //当只有一个参数时可以不写
    d("da", 18) { bame:String, age:Int ->
        "$bame"
    }
    fun ada(name:String, age:Int):String{
        return "$name $age"
    }
    //两个冒号可以传递具名函数的引用
    d("da", 18, ::ada);
    //闭包
    fun lai():(String)->String{
        val hour = (1..24).shuffled().last()
        return {
            "$it $hour"
        }
    }
}
//可空性
fun test6(){
    var a : String? = "dsad"
    a = null
    var b = null
    val c = null
    //安全调用操作符
    a?.capitalize()
    a = a?.let {
        //不为空白字符串
        if(it.isNotBlank()){
            it.capitalize()
        }else {
            "sa"
        }
    }
    //非空断言操作符
    a!!.capitalize()
    //空合并操作符 如果左边为空  返回右边
    val d = a ?: "das"
}

fun test7(){
    var a = "dsadsadsa"
    var indexOf = a.indexOf('d')
    var str = a.substring(0, indexOf)
    var b = a.substring(0 until indexOf)
    var split = a.split("d")
    //解构赋值
    val (c, d, e) = split
    val f = "dsadsadsa"
    f.replace(Regex("[aeiou]"), {
        when(it.value) {
            "a" ->"8"
            else -> it.value
        }
    })

    var i = "jack"
    var o = "jack"
    //下面都返回true  常量池
    println(i == o)
    println(i === o)
    f.forEach { print(it) }
}

fun test8(){
    //返回null
    val a = "8.89".toIntOrNull()
    //四舍五入转int
    val b = 8.9.roundToInt()
    //四舍五入格式化
    val c:String = "%.2f".format(8.956)
}

fun test9(){
    //apply配置函数  对这个对象进行配置
    val a:File = File("").apply {
        setReadable(true)
    }
    //let函数
    val b:Int = listOf<Int>(3,2,1).first().let {
        it * it
    }
    fun toLong(name:String) = name.length >= 10
    //run函数
    val c:Boolean = File("").run {
        readText().contains("great")
    }
    val d = "dasdsa".run(::toLong)
    //with函数
    val e:Boolean = with("dasdaa"){
        length >= 10
    }
    //also函数
    val f:File = File("").also {
        println(it.name)
    }.also {
        println(it.length())
    }.also {
        it.apply {
            setReadable(true)
        }
    }
    //takeif  判断lambda表达式的返回结果 true false  true返回接收者对象  否则返回null
    val g:File? = File("")
            .takeIf {
                it.exists() && it.canRead()
            }
    //takeunless和takeIf的返回结果相反
}

fun test10(){
    var a = listOf("dsa", 1, 18.8)
    var value = a[0]
    println(a.getOrElse(3){"没有这个元素"})
    println(a.getOrNull(3) ?: "没有这个元素")
    //可变列表
    val b = mutableListOf("das", 1 , 1.8)
    b.add("das")
    b.remove(1)
    b += "dsa"
    b -= 1
    b.removeIf({
        it == 1
    })
    for (item in b) {
        println(item)
    }
    b.forEach{
        println(it)
    }
    b.forEachIndexed{
        index, item ->
        println(index)
    }
    val (c,_,d) = b;
}

public fun test11(){
    val set = setOf("dsa", 'd', 18, 18.6)
    set.elementAt(2)
    val a = mutableSetOf("dsa", 'd', 18, 18.6)
    a += "das"
    a -= 18
    listOf("a", 'a', 18).toSet().toList()
    //去重返回新的list
    listOf("a", 'a').distinct()
}

public fun test12(){
    //数组类型
    val a = intArrayOf(1,2,3)
    listOf(1,2,3).toIntArray()
    val b = arrayOf("", 'c')
}

fun test13(){
    //map类型
    val a = mapOf("name" to "建哥", "age" to 18)
    mapOf(Pair("name","李子建"), Pair("age", 18))
    a["name"]
    a.get("name")
    a.getValue("name")
    a.getOrElse("name"){"jiange"}
    for (entry in a) {
        println(entry.key)
        println(entry.value)
    }
    a.forEach {
        println("${it.key} ${it.value}")
    }
    a.forEach { key, value ->
        println("$key,$value")
    }
    val b = mutableMapOf(Pair("name", "Lizijian"))
    b += "age" to "18"
    b.put("name", "李子建")
    b.getOrPut("sex"){"男"}
}

//每个属性都会自动的为其创建get和set方法
class A(
        _name:String = "李子建",
        var age: Int,
        isNormal:Boolean
) {
    var name = _name
    get(){
        return field+"da"
    }
    private set(value) {
        field = value.trim()
    }
    //延迟初始化
    lateinit var sex:String

    fun ready(){
        sex = "男"
    }

    fun battle(){
        if(::sex.isInitialized) {
            println(sex)
        }
    }

    //惰性初始化  在第一次使用的时候才去调用初始化函数
    private fun load():String{
        return "da"
    }

    val config:String by lazy {load()}

    //次构造函数可以写多个
    constructor(name:String) : this(name, age = 10, isNormal = false) {
        this.name = name.toUpperCase()
    }
    init {
        //这个就是Java的初始化代码块
        //在构建对象的时候执行
        require(age > 0) {"年龄太小"}
    }

    /*
        初始化顺序
        1. 主构造函数里声明的属性
        2. 类级别的属性
        3. 初始化代码块的赋值
        4. 次构造函数的赋值
     */
}


fun test14(){
    var a = A("",18, false)
    var b = A(age = 10, isNormal = false)
//    a.name = "jian"
    //调用次构造函数
    var c = A("jian")
    val d = "da"
}
open class B (
        val name:String
) {
    operator fun component1() = name
    open fun load() = println("load...")
}
class C : B(name = "李子建") {
    override fun load() {
        super.load()
        return
    }
}

fun test15(){
    val a = C()
    println(a is C)
    println(a is B)
    (a as C).load()
    //用到了Object 默认是单例
    val b = object : B (name = "lizijian") {
        override fun load() {
            super.load()
            println("匿名内部类")
            return
        }
    }
}
//kotlin的所有类的超类是Any类

//用object修饰默认是单例
object SingleCompelete{
    fun load() = println("da")
}
//伴生对象
//只有实例化D或者是调用load函数才会实例化伴生对象  可以节省内存  并且这个对象是单例的
open class D{
    companion object{
        private const val PATH = "das"

        fun load() = println(PATH)
    }
}
class E{
    inner class inner{
        fun a(){
            println("内部类inner")
        }
    }
    fun a() = println("外部函数a")
}
//数据类 提供了默认的toString的特殊实现  以及equals和HashCode的特殊实现  ==利用equals比较的是结构
//同时提供了一个copy函数  在调用copy的时候 是不调用次构造函数的
//提供了默认的解构赋值
data class F(
        var x:Int,
        var y:Int
){
    //重载＋号 plus是+号的名称
    operator fun plus(other:F) = F(this.x+other.x, this.y+other.y)
}
enum class G(name: String){
    EAST("东"),
    NORTH("北"),
    SOUTH("南"),
    WEST("西")
}
sealed class H{
    object a : H()
    object b : H()
    class c(val id:String) : H()
}
fun test16(){
    //既是类名  也是实例名
    SingleCompelete.load()
    D.load()
    E().inner().a()
    println(F(x = 10, y = 20))
    val (x, y) = F(x = 10, y = 20)
    G.EAST
    fun a(state:H){
        when(state){
            is H.a -> "a"
            is H.b -> "b"
            is H.c -> "c ${(state as H.c).id}"
        }
    }
    fun b(state:G){
        when(state){
            G.EAST -> ""
            G.WEST -> ""
            G.NORTH -> "d"
            G.SOUTH -> ""
        }
    }
}
interface Moveable{
    val maxSpeed:Int
        get() {
            return (1..500).shuffled().first()
        }
    val wheels:Int

    fun move(moveable: Moveable) : String
}
class Car (
        var name: String,
        override var wheels: Int
) : Moveable {
    override var maxSpeed: Int
        get() = super.maxSpeed
        set(value) {
            this.maxSpeed = value
        }
    override fun move(moveable: Moveable): String {
        return "${moveable.maxSpeed}超了不行"
    }
}

//抽象类
abstract class Person(
        var name:String,
        var age:Int
){
    abstract fun eat():Unit
}
class Lai(
        var a: String,
        var b: Int
) : Person(a, b){
    override fun eat(){

    }
}

//泛型类
//使用out协变在kotlin 泛型的类型可以是定义的子类
//父类泛型对象可以赋值给子类用in 子类赋值给父类用out
//最后调用的时候还是调用实际赋值的类型
//reified必须要和inline一起使用  默认情况不使用这个关键字不能进行一些对泛型的操作  因为在此之前泛型会被替换
class J <T:B,R> (vararg name:T, age: Int) {
    fun a():R?{
        return null
    }
    //out叫做协变  如果泛型只作为函数的返回输出 就使用out
    //in叫做逆变 如果泛型只作为函数的入参输入 使用in
    var a:Array<out T> = name
    inline fun <reified F> b (c:F) {

    }
}

//可变参数
fun K(vararg a:Int){
    println(a[0])
}

//扩展对象  下面是给String扩展一个函数
fun String.addText(other:Int):String {
    return this + "!".repeat(other)
}
fun <T> T.easy():T{
    println(this)
    return this
}
fun Any.esayPrintln(){
    println("$this 😍")
}
//扩展属性
var String.numVel
    get() = count { "aeiou".contains(it) }
    set(value) {
        this.numVel = value
    }
//可空类型的扩展
//infix适合有单个参数的扩展和类函数 可以更简洁的调用
infix fun String?.printDefault(default: String) = print(this?:default)
fun test17(){
    var addText = "da".addText(10)
    "a".easy()
    "dad" printDefault ("李子建")
}

fun test18(){
    //map函数
    var a = listOf("李子建", "建")
    a.map { name -> "帅$name" }
    //扁平化list 返回一重list
    var b = listOf(listOf(""))
    b.flatMap {
        it
    }
    //过滤 返会false的元素将被移除
    a.filter {
        it == "建"
    }
    b.flatMap { it.filter { it == "jian" } }
    //合并函数zip两个集合合并成map键值对 list<Pair>  toMap转换成map
    a.zip(b)
    //fold做累加  接受一个参数为初始累加值
    a.fold(""){last, now ->
        last + now
    }
    generateSequence (2) { value ->
        value + 1
    }.take(100).toList()
}

//调用Java
fun test19(){
    val a:Student = Student()
    //返回的类型为String!平台类型
    a.sayHello()
    a.javaClass
    //属性是私有时 要添加get函数


}
@JvmOverloads
@Throws(IOException::class)
fun test20(left:Int = 10){
    throw IOException()
}
class Test(
        @JvmField
        var name:String
){
    companion object {
        @JvmField
        var MAX = 10
        @JvmStatic
        fun getSpeel(){
            println(MAX)
        }
    }
}
@JvmField
val JianJian = {name:String, age:Int ->
    "$name $age"
}
fun jianjian() {
    var a : Int?

}
/**
 * JvmField作用在kotlin中的属性或者是方法上，可以将方法避免编译城Java的时候变成私有的，标注后回变为public
 * JvmFile可以标注再kotlin的首行，这样编译为Java的时候的类的名字就是注解的传递的值
 * JvmStatic 标注在kotlin的方法上，表示这个方法变为静态方法
 * JvmOverride  标注在kotlin的方法上，如果这个方法有默认参数，在Java中是默认必须要传递完全部的参数的，
 * 使用这个注解，可以为这个方法生成对应的重载方法
 */

























